#ifndef __IM_TYPE_H__
#define __IM_TYPE_H__

#include <im_base_types.h>

/**
 * @defgroup IMTypeClass
 * @ingroup PublicIME
 * @{
 */

/** @struct IMTypeClass
 * @brief The base struct of all type class structs.
 *
 * A type can be a fundamental type, such as char, int, etc.
 * or a class type, such as IMObject, IMEvent, etc.
 *
 * A class type must be derived from a inheritable type, which
 * can be another class type or IM_TYPE_INVALID.
 *
 * IM_TYPE_INVALID, is not a type. It just acts as the only root
 * of all class types.
 *
 * A class type must have a class struct which contains
 * IMTypeClass as the first member.
 *
 * IMTypeClass has only one member, _type, the type id of this
 * class.
 */

/** @struct IMTypeInstance
 * @brief The base struct of all class type instances.
 *
 * Instances of any class type must have an instance struct which
 * contains IMTypeInstance as the first member.
 *
 * IMTypeInstance has only one member, _class, pointer to the
 * class struct, from which the instance is instantiated.
 */

IM_BEGIN_DECLS

/**
 * @brief Predefined built-in types, including all fundamental
 * types, as well as various class types.
 */
enum
{
    IM_TYPE_INVALID = 0,
    IM_TYPE_VOID,           /**< A placeholder type >**/
    IM_TYPE_CHAR,
    IM_TYPE_UCHAR,
    IM_TYPE_BOOL,
    IM_TYPE_INT16,
    IM_TYPE_UINT16,
    IM_TYPE_INT32,
    IM_TYPE_UINT32,
    IM_TYPE_INT64,
    IM_TYPE_UINT64,
    IM_TYPE_DOUBLE,
    IM_TYPE_C_STRING,       /**< Plain old C string, not IM_TYPE_STRING object >**/
    IM_TYPE_POINTER,
    IM_TYPE_BYTE_ARRAY,
    IM_TYPE_BOOL_ARRAY,
    IM_TYPE_INT16_ARRAY,
    IM_TYPE_UINT16_ARRAY,
    IM_TYPE_INT32_ARRAY,
    IM_TYPE_UINT32_ARRAY,
    IM_TYPE_INT64_ARRAY,
    IM_TYPE_UINT64_ARRAY,
    IM_TYPE_DOUBLE_ARRAY,
    IM_TYPE_LAST_FUNDAMENTAL = IM_TYPE_DOUBLE_ARRAY,

    IM_TYPE_OBJECT = 32,
    IM_TYPE_INITIALLY_OWNED,
    IM_TYPE_VALUE,
    IM_TYPE_VALUE_ARRAY,
    IM_TYPE_OBJECT_ARRAY,
    IM_TYPE_OBJECT_QUEUE,
    IM_TYPE_MEMORY_CHUNK,
    IM_TYPE_STREAM,
    IM_TYPE_BYTE_STREAM,
    IM_TYPE_TEXT_STREAM,

    IM_TYPE_ATTRIBUTE,
    IM_TYPE_ATTR_LANGUAGE,
    IM_TYPE_ATTR_FONT_FAMILY,
    IM_TYPE_ATTR_FONT_STYLE,
    IM_TYPE_ATTR_FONT_WEIGHT,
    IM_TYPE_ATTR_FONT_SIZE,
    IM_TYPE_ATTR_FONT_SCALE,
    IM_TYPE_ATTR_FOREGROUND,
    IM_TYPE_ATTR_BACKGROUND,
    IM_TYPE_ATTR_UNDERLINE,
    IM_TYPE_ATTR_UNDERLINE_COLOR,
    IM_TYPE_ATTR_STRIKETHROUGH,
    IM_TYPE_ATTR_STRIKETHROUGH_COLOR,
    IM_TYPE_ATTR_HIGHLIGHT,
    IM_TYPE_ATTR_REVERSE,
    IM_TYPE_ATTR_STRING,
    IM_TYPE_ATTR_OBJECT,
    IM_TYPE_ATTR_LIST,

    IM_TYPE_STRING,
    IM_TYPE_TEXT,
    IM_TYPE_CANDIDATE,
    IM_TYPE_LOOKUP_TABLE,
    IM_TYPE_PROPERTY,
    IM_TYPE_PROP_LIST,
    IM_TYPE_EVENT,
    IM_TYPE_EVENT_ROLES,
    IM_TYPE_HOTKEY,
    IM_TYPE_HOTKEY_PROFILE,
    IM_TYPE_COMPONENT_INFO,

    IM_TYPE_IO_CHANNEL,
    IM_TYPE_IO_CHANNEL_UNIX,

    IM_TYPE_CONNECTION,
    IM_TYPE_MAIN_LOOP,
    IM_TYPE_MAIN_LOOP_UNIX,
    IM_TYPE_SERVER,
    IM_TYPE_SERVER_UNIX,
    IM_TYPE_ADDRESS,
    IM_TYPE_ADDRESS_UNIX,

    IM_TYPE_MODULE,

    IM_TYPE_LAST_BUILTIN = IM_TYPE_MODULE,

    IM_TYPE_FIRST_USER_DEFINED = IM_TYPE_LAST_BUILTIN + 1
};

typedef void    (*IMInstanceInitFunc)       (IMPointer instance);
typedef void    (*IMInstanceFinalizeFunc)   (IMPointer instance);
typedef void    (*IMClassInitFunc)          (IMPointer klass);
typedef void    (*IMClassFinalizeFunc)      (IMPointer klass);

typedef struct _IMTypeClass        IMTypeClass;
typedef struct _IMTypeInstance     IMTypeInstance;
typedef struct _IMTypeInfo         IMTypeInfo;

struct _IMTypeClass
{
    IMType type;
};

struct _IMTypeInstance
{
    const IMTypeClass *klass;      /**< Pointer to TypeClass >**/
};

struct _IMTypeInfo
{
    IMSize                  class_size;
    IMClassInitFunc         class_init;
    IMClassFinalizeFunc     class_finalize;

    IMSize                  instance_size;
    IMInstanceInitFunc      instance_init;
    IMInstanceFinalizeFunc  instance_finalize;
};

/**
 * @brief Initialize the IMBus type system.
 *
 * This function must be called before using any other IMBus functions.
 */
void            im_type_init                ();

/**
 * @brief Register a new class.
 *
 * Register a new class into the type system.
 *
 * @param parent            Type of parent class.
 * @param class_name        Name of the new class, must be a static string.
 * @param type_info         Information of the type.
 * @param instantiatable    Whether it's a instantiatable class.
 *                          A class with pure virtual methods can't be instantiated.
 *
 * @return Type of the new class.
 */
IMType          im_type_register_class      (IMType              parent,
                                             const IMChar       *class_name,
                                             const IMTypeInfo   *type_info,
                                             IMBool              instantiatable);

/**
 * @brief Deregister a class.
 *
 * A class can only be deregistered if there are no instances instantiated from
 * it and no classes derived from it registered.
 *
 * @param type      The type to deregister.
 */
IMBool          im_type_deregister_class    (IMType              type);

/**
 * @brief Get the name of a type.
 * @param type      The type to get the name from.
 */
const IMChar*   im_type_get_name            (IMType              type);

/**
 * @brief Get a type by its name.
 * @param name      The name of the type to get.
 */
IMType          im_type_from_name           (const IMChar       *name);

/**
 * @brief Get the parent type of a type.
 * @param type      The type to be checked.
 */
IMType          im_type_get_parent          (IMType              type);

/**
 * @brief Check whether a type is a fundamental type.
 *
 * Fundamental types include void, char, bool, int32, int64, double,
 * string, byte array, etc.
 *
 * @param type      The type to be checked.
 */
IMBool          im_type_is_fundamental      (IMType             type);

/**
 * @brief Check whether a type is a built-in type.
 *
 * A built-in type is a type defined inside libimbus, which has a
 * constant type ID.
 *
 * @param type      The type to be checked.
 */
IMBool          im_type_is_built_in         (IMType             type);

/**
 * @brief Check whether a type is instantiatable or not.
 *
 * A type is only instantiatable if it is either a fundamental type, or a
 * non-pure virtual class.
 *
 * @param type      The type to be checked.
 */
IMBool          im_type_is_instantiatable   (IMType             type);

/**
 * @brief Get the instance size of a type.
 * @param type      The type to be checked.
 */
IMSize          im_type_get_instance_size   (IMType             type);

/**
 * @brief Check whether a type is derived from another type or not.
 *
 * @param type      The type to be checked.
 * @param is_a_type A parent type.
 * @return True if type is derived from is_a_type.
 */
IMBool          im_type_is_a                (IMType              type,
                                             IMType              is_a_type);
/**
 * @brief Check whether a specified type is referenced.
 *
 * A type is referenced if an instance of this type is created,
 * or if this type is inherited by another type.
 *
 * @param type      The type to be checked.
 */
IMBool          im_type_is_referenced       (IMType              type);
/**
 * @brief  Check whether a specified type is a valid type.
 * @param type      The type to be checked.
 */
IMBool          im_type_is_valid            (IMType              type);
/**
 * @brief Check whether a pointer is pointed to a valid class struct
 *        instance or not.
 * @param klass      The pointer to be checked.
 */
IMBool          im_type_class_valid         (IMConstPointer      klass);

/**
 * @brief Get the name of an type class struct.
 * @param klass Pointer to the type class.
 */
const IMChar*   im_type_class_get_name      (IMConstPointer      klass);

/**
 * @brief Get the type of an type class struct.
 * @param klass Pointer to the type class.
 */
IMType          im_type_class_get_type      (IMConstPointer      klass);

/**
 * @brief Get the parent of an type class struct.
 * @param klass Pointer to the type class.
 */
IMConstPointer  im_type_class_get_parent    (IMConstPointer      klass);

/**
 * @brief Get a type class struct instance by its name.
 * @param name      The name of the type class struct instance to get.
 */
IMConstPointer  im_type_class_from_name     (const IMChar       *name);

/**
 * @brief Get a type class struct instance by its type.
 * @param type      The type of the type class struct instance to get.
 */
IMConstPointer  im_type_class_from_type     (IMType              type);

/**
 * @brief Check whether a type class is derived from
 *        a parent class type or not.
 * @param klass     A pointer to the type class struct to be checked.
 * @param is_a_type The parent type.
 * @return True if klass is derived from is_a_type.
 */
IMBool          im_type_class_is_a          (IMConstPointer      klass,
                                             IMType              is_a_type);

/**
 * @brief Cast a type class pointer to a parent type.
 * @param klass     The type class pointer to be casted.
 * @param is_a_type The parent type.
 * @return If cast can be done then klass will be returned, otherwise return 0.
 */
IMConstPointer  im_type_class_check_cast    (IMConstPointer      klass,
                                             IMType              is_a_type);

/**
 * @brief Return the size of a type class.
 * @param klass The type class pointer.
 */
IMSize          im_type_class_sizeof        (IMConstPointer      klass);

/**
 * @brief Create a new type instance for a given type.
 *
 * This function will allocate required memory and call constructors of
 * the given class type and its parent class types in the correct sequence
 * to initialize the instance.
 *
 * @param type  Type of the class.
 * @return A pointer to the newly created instance.
 */
IMPointer       im_type_instance_new        (IMType              type);

/**
 * @brief Delete a type instance.
 *
 * @param instance Pointer to the instance.
 */
void            im_type_instance_delete     (IMPointer           instance);

/**
 * @brief Get the size of a type instance.
 * @param instance Pointer to the instance.
 */
IMSize          im_type_instance_sizeof     (IMConstPointer      instance);

/**
 * @brief Check whether a type instance is valid or not.
 * @param instance Pointer to the instance to be checked.
 */
IMBool          im_type_instance_valid      (IMConstPointer      instance);

/**
 * @brief Get the class type of a type instance.
 * @param instance Pointer to the instance.
 */
IMType          im_type_instance_get_type   (IMConstPointer      instance);

/**
 * @brief Get class struct pointer of a type instance.
 * @param instance Pointer to the instance.
 */
IMConstPointer  im_type_instance_get_class  (IMConstPointer      instance);

/**
 * @brief Check whether a type instance inherits a parent type.
 * @param instance Pointer to the instance to be checked.
 * @param is_a_type The type to check for parenthood.
 */
IMBool          im_type_instance_is_a       (IMConstPointer      instance,
                                             IMType              is_a_type);

/**
 * @brief Cast a type instance to a parent type.
 * @param instance Pointer to the instance.
 * @param is_a_type The type to cast as parent.
 * @return the instance itself if cast can be done, otherwise 0.
 */
IMPointer       im_type_instance_check_cast (IMPointer           instance,
                                             IMType              is_a_type);

/**
 * @brief Cast a const type instance to a parent type.
 * @param instance Pointer to the instance.
 * @param is_a_type The type to cast as parent.
 * @return the type instance itself if cast can be done, otherwise 0.
 */
IMConstPointer  im_type_instance_check_cast_const   (IMConstPointer      instance,
                                                     IMType              is_a_type);

/**
 * @brief Print information about all known types.
 *
 * This function is only useful for debugging the type system.
 */
void            im_type_print_all_type_informations ();


/*******************< type macros >****************/
#define IM_TYPE_INSTANCE_CHECK_CAST(instance,type,cast_type) \
    ((cast_type*)im_type_instance_check_cast(instance, type))

#define IM_TYPE_INSTANCE_CHECK_CAST_CONST(instance,type,cast_type) \
    ((const cast_type*)im_type_instance_check_cast_const(instance, type))

#define IM_TYPE_CLASS_CHECK_CAST(klass,type,cast_type) \
    ((const cast_type*)im_type_class_check_cast(klass, type))

#define IM_TYPE_INSTANCE_CHECK_TYPE(instance,type) \
    (im_type_instance_is_a(instance,type))

#define IM_TYPE_CLASS_CHECK_TYPE(klass,type) \
    (im_type_class_is_a(klass,type))

#define IM_TYPE_INSTANCE_GET_CLASS_CAST(instance,type,cast_type) \
    (IM_TYPE_CLASS_CHECK_CAST(im_type_instance_get_class(instance),type,cast_type))

IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
